﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Markup;
using Buddy.Coroutines;
using HREngine.Bots;
using IronPython.Modules;
using log4net;
using Microsoft.Scripting.Hosting;
using Triton.Bot;
using Triton.Common;
using Triton.Game;
using Triton.Game.Data;

//!CompilerOption|AddRef|IronPython.dll
//!CompilerOption|AddRef|IronPython.Modules.dll
//!CompilerOption|AddRef|Microsoft.Scripting.dll
//!CompilerOption|AddRef|Microsoft.Dynamic.dll
//!CompilerOption|AddRef|Microsoft.Scripting.Metadata.dll
using Triton.Game.Mapping;
using Triton.Bot.Logic.Bots.BattleBot;
using Logger = Triton.Common.LogUtilities.Logger;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace HREngine.Bots
{
    public class HandCardItem : INotifyPropertyChanged
    {
        private int position = -1, attack = -1, health = -1, addAttack = -1, addHealth = -1, entityID = -1;
        private string chnName = "未知卡牌";
        private TAG_RACE race = TAG_RACE.INVALID;
        private string effect = "";

        public HandCardItem()
        {
        }

        public void GetEffect(Minion minion)
        {
            if (minion.poisonous) effect += "☢";
            if (minion.windfury) effect += "💨";
            if (minion.taunt) effect += "🔰";
            if (minion.reborn) effect += "💕";
            if (minion.divineshild) effect += "💛";
            if (minion.frenzy) effect += "💖";
            OnPropertyChanged("Effect");
        }

        public TAG_RACE Race
        {
            get
            {
                return race;
            }
            set
            {
                race = value;
                OnPropertyChanged("Race");
            }
        }
        public string Effect
        {
            get
            {
                return effect;
            }
            set
            {
                effect = value;
                OnPropertyChanged("Effect");
            }
        }

        public string ChnName
        {
            get
            {
                return chnName;
            }
            set
            {
                chnName = value;
                OnPropertyChanged("ChnName");
            }
        }

        public int Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
                OnPropertyChanged("Position");
            }
        }

        public int Attack
        {
            get
            {
                return attack;
            }
            set
            {
                attack = value;
                OnPropertyChanged("Attack");
            }
        }

        public int AddAttack
        {
            get
            {
                return addAttack;
            }
            set
            {
                addAttack = value;
                OnPropertyChanged("AddAttack");
            }
        }

        public int Health
        {
            get
            {
                return health;
            }
            set
            {
                health = value;
                OnPropertyChanged("Health");
            }
        }

        public int AddHealth
        {
            get
            {
                return addHealth;
            }
            set
            {
                addHealth = value;
                OnPropertyChanged("AddHealth");
            }
        }

        public int EntityID
        {
            get
            {
                return entityID;
            }
            set
            {
                entityID = value;
                OnPropertyChanged("EntityID");
            }
        }


        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;


        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        /* INotifyPropertyChanged implementation */
    }

    public class BattleRoutine : IRoutine
    {
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();
        private readonly ScriptManager _scriptManager = new ScriptManager();

        private readonly List<Tuple<string, double>> _mulliganRules = new List<Tuple<string, double>>();
        private List<Triton.Game.Mapping.TAG_RACE> _includedRaces;

        private int dirtyTargetSource = -1;
        private int stopAfterWins = 30;
        private int concedeLvl = 5; // the rank, till you want to concede
        private int dirtytarget = -1;
        private int dirtychoice = -1;
        private string choiceCardId = "";
        DateTime starttime = DateTime.Now;
        bool enemyConcede = false;
        bool hasOperation = false;

        bool firstMove = true;
        bool firstTurn = true;

        public bool learnmode = false;
        public bool printlearnmode = true;

        Silverfish sf = Silverfish.Instance;
        BattleBotSettings botset
        {
            get { return BattleBotSettings.Instance; }
        }
        //uncomment the desired option, or leave it as is to select via the interface
        Behavior behave = new BehaviorControl();
        //Behavior behave = new BehaviorRush();


        public BattleRoutine()
        {
            // Global rules. 
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_01", 0));        // 艾德温·范克里夫
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_02", 0));        // 迦拉克隆
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_08", 0));        // 伊利丹·怒风
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_10", 0));        // 贸易大王加里维克斯
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_11", 4.27));     // 炎魔之王拉格纳罗斯
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_12", 0));        // 鼠王
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_14", 4.35));     // 瓦托格尔女王
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_15", 4.20));     // 堕落的乔治
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_16", 0));        // 挂机的阿凯
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_17", 0));        // 米尔菲丝·法力风暴
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_18", 4.26));     // 海盗帕奇斯
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_19", 0));        // 老蓟皮
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_20", 0));        // 普崔塞德教授
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_21", 4.24));     // 伟大的阿卡扎曼扎拉克
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_22", 4.33));     // 巫妖王
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_23", 0));        // 沙德沃克
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_25", 0));        // 巫妖巴兹亚尔
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_27", 0));        // 辛达苟萨
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_28", 0));        // 永恒者托奇
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_30", 0));        // 奈法利安
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_31", 0));        // 调酒机器人
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_33", 0));        // 馆长
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_34", 0));        // 帕奇维克
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_35", 0));        // 尤格-萨隆
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_36", 0));        // 舞者达瑞尔
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_37", 0));        // 加拉克苏斯大王
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_38", 0));        // 穆克拉
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_39", 0));        // 疯狂金字塔
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_40", 4.43));     // 芬利·莫格顿爵士
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_41", 4.47));     // 雷诺·杰克逊
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_42", 0));        // 伊莉斯·逐星
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_43", 0));        // 恐龙大师布莱恩
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_44", 0));        // 希尔瓦娜斯·风行者
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_45", 4.18));     // 至尊盗王拉法姆
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_47", 0));        // 提里奥·弗丁
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_49", 4.22));     // 米尔豪斯·法力风暴
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_50", 0));        // 苔丝·格雷迈恩
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_52", 0));        // 死亡之翼
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_53", 0));        // 伊瑟拉
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_55", 0));        // 菌菇术士弗洛格尔
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_56", 0));        // 阿莱克丝塔萨
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_57", 0));        // 诺兹多姆
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_58", 0));        // 玛里苟斯
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_59", 0));        // 阿兰娜·逐星
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_60", 4.49));     // 凯尔萨斯·逐日者
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_61", 0));        // 瓦丝琪女士
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_62", 4.03));     // 玛维·影歌
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_64", 0));        // 尤朵拉船长
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_67", 4.04));     // 钩牙船长
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_68", 0));        // 天空上尉库拉格
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_70", 0));        // 比格沃斯先生
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_71", 4.36));     // 詹迪斯·巴罗夫
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_72", 0));        // 巴罗夫领主
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_74", 3.94));     // 林地守护者欧穆
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_75", 0));        // 拉卡尼休
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_76", 4.19));     // 奥拉基尔
            _mulliganRules.Add(new Tuple<string, double>("TB_BaconShop_HERO_78", 0));        // 齐恩瓦拉

            //bool concede = false;
            bool teststuff = false;
            // set to true, to run a testfile (requires test.txt file in folder where _cardDB.txt file is located)
            bool printstuff = true; // if true, the best board of the tested file is printet stepp by stepp

            Helpfunctions.Instance.ErrorLog("----------------------------");
            Helpfunctions.Instance.ErrorLog("您正在使用的AI版本为" + Silverfish.Instance.versionnumber);
            Helpfunctions.Instance.ErrorLog("----------------------------");

            if (teststuff)
            {
                Ai.Instance.autoTester(printstuff);
            }
        }

        #region Scripting

        private const string BoilerPlateExecute = @"
import sys
sys.stdout=ioproxy

def Execute():
    return bool({0})";

        public delegate void RegisterScriptVariableDelegate(ScriptScope scope);

        public bool GetCondition(string expression, IEnumerable<RegisterScriptVariableDelegate> variables)
        {
            var code = string.Format(BoilerPlateExecute, expression);
            var scope = _scriptManager.Scope;
            var scriptSource = _scriptManager.Engine.CreateScriptSourceFromString(code);
            scope.SetVariable("ioproxy", _scriptManager.IoProxy);
            foreach (var variable in variables)
            {
                variable(scope);
            }
            scriptSource.Execute(scope);
            return scope.GetVariable<Func<bool>>("Execute")();
        }

        public bool VerifyCondition(string expression,
            IEnumerable<string> variables, out Exception ex)
        {
            ex = null;
            try
            {
                var code = string.Format(BoilerPlateExecute, expression);
                var scope = _scriptManager.Scope;
                var scriptSource = _scriptManager.Engine.CreateScriptSourceFromString(code);
                scope.SetVariable("ioproxy", _scriptManager.IoProxy);
                foreach (var variable in variables)
                {
                    scope.SetVariable(variable, new object());
                }
                scriptSource.Compile();
            }
            catch (Exception e)
            {
                ex = e;
                return false;
            }
            return true;
        }

        #endregion

        #region Implementation of IAuthored

        /// <summary> The name of the routine. </summary>
        public string Name
        {
            get { return "Battle Grounds Strategies"; }
        }

        /// <summary> The description of the routine. </summary>
        public string Description
        {
            get { return "For Personal Uses Only."; }
        }

        /// <summary>The author of this routine.</summary>
        public string Author
        {
            get { return "BXYMartin"; }
        }

        /// <summary>The version of this routine.</summary>
        public string Version
        {
            get { return "0.0.0.1"; }
        }

        #endregion

        #region Implementation of IBase

        /// <summary>Initializes this routine.</summary>
        public void Initialize()
        {
            _scriptManager.Initialize(null,
                new List<string>
                {
                    "Triton.Game",
                    "Triton.Bot",
                    "Triton.Common",
                    "Triton.Game.Mapping",
                    "Triton.Game.Abstraction"
                });
        }

        /// <summary>Deinitializes this routine.</summary>
        public void Deinitialize()
        {
            _scriptManager.Deinitialize();
        }

        #endregion

        #region Implementation of IRunnable

        /// <summary> The routine start callback. Do any initialization here. </summary>
        public void Start()
        {
            firstTurn = true;
            GameEventManager.NewGame += GameEventManagerOnNewGame;
            GameEventManager.GameOver += GameEventManagerOnGameOver;
            GameEventManager.QuestUpdate += GameEventManagerOnQuestUpdate;
            GameEventManager.ArenaRewards += GameEventManagerOnArenaRewards;

            if (Hrtprozis.Instance.settings == null)
            {
                Hrtprozis.Instance.setInstances();
                ComboBreaker.Instance.setInstances();
                PenalityManager.Instance.setInstances();
            }
            behave = sf.getBehaviorByName(BattleRoutineSettings.Instance.DefaultBehavior);
        }

        /// <summary> The routine tick callback. Do any update logic here. </summary>
        public void Tick()
        {
        }

        /// <summary> The routine stop callback. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            GameEventManager.NewGame -= GameEventManagerOnNewGame;
            GameEventManager.GameOver -= GameEventManagerOnGameOver;
            GameEventManager.QuestUpdate -= GameEventManagerOnQuestUpdate;
            GameEventManager.ArenaRewards -= GameEventManagerOnArenaRewards;
        }

        #endregion

        #region Implementation of IConfigurable

        /// <summary> The routine's settings control. This will be added to the Hearthbuddy Settings tab.</summary>
        public UserControl Control
        {
            get
            {
                using (var fs = new FileStream(@"Routines\BattleRoutine\SettingsGui.xaml", FileMode.Open))
                {
                    var root = (UserControl)XamlReader.Load(fs);

                    // Your settings binding here.

                    // defaultBehaviorComboBox1
                    if (
                        !Wpf.SetupComboBoxItemsBinding(root, "defaultBehaviorComboBox1", "AllBehav",
                            BindingMode.OneWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupComboBoxItemsBinding failed for 'defaultBehaviorComboBox1'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }



                    if (
                        !Wpf.SetupComboBoxSelectedItemBinding(root, "defaultBehaviorComboBox1",
                            "DefaultBehavior", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupComboBoxSelectedItemBinding failed for 'defaultBehaviorComboBox1'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // 表情
                    if (
                        !Wpf.SetupComboBoxItemsBinding(root, "emoteComboBox", "AllEmote",
                            BindingMode.OneWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupComboBoxItemsBinding failed for 'emoteComboBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (
                        !Wpf.SetupComboBoxSelectedItemBinding(root, "emoteComboBox",
                            "DefaultEmote", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupComboBoxSelectedItemBinding failed for 'emoteComboBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }
                    // Your settings event handlers here.
                    if (
                        !Wpf.SetupListBoxItemsBinding(root, "HandCardListBox",
                            "HandCardList", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupListBoxItemsBinding failed for 'HandCardListBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (
                        !Wpf.SetupListBoxItemsBinding(root, "BattleGroundListBox",
                            "BattleGroundList", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupListBoxItemsBinding failed for 'BattleGroundListBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (
                        !Wpf.SetupListBoxItemsBinding(root, "BaconShopListBox",
                            "BaconShopList", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupListBoxItemsBinding failed for 'BaconShopListBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    if (
                        !Wpf.SetupListBoxItemsBinding(root, "HeroListBox",
                            "HeroList", BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupListBoxItemsBinding failed for 'HeroListBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // MaxWide
                    if (
                        !Wpf.SetupTextBoxBinding(root, "MaxWideTextBox", "MaxWide",
                        BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat("[SettingsControl] SetupTextBoxBinding failed for 'MaxWideTextBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // 打印自定义惩罚值
                    if (
                        !Wpf.SetupCheckBoxBinding(root, "PrintPenaltiesCheckBox",
                            "UsePrintPenalties",
                            BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupCheckBoxBinding failed for 'PrintPenaltiesCheckBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // 打印最终出牌
                    if (
                        !Wpf.SetupCheckBoxBinding(root, "PrintNextMoveCheckBox",
                            "UsePrintNextMove",
                            BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupCheckBoxBinding failed for 'PrintNextMoveCheckBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    // 清除信息
                    if (
                        !Wpf.SetupCheckBoxBinding(root, "ClearStatusCheckBox",
                            "UseClearStatus",
                            BindingMode.TwoWay, BattleRoutineSettings.Instance))
                    {
                        Log.DebugFormat(
                            "[SettingsControl] SetupCheckBoxBinding failed for 'ClearStatusCheckBox'.");
                        throw new Exception("The SettingsControl could not be created.");
                    }

                    return root;
                }
            }
        }

        /// <summary>The settings object. This will be registered in the current configuration.</summary>
        public JsonSettings Settings
        {
            get { return BattleRoutineSettings.Instance; }
        }

        #endregion

        #region Implementation of IRoutine

        /// <summary>
        /// Sends data to the routine with the associated name.
        /// </summary>
        /// <param name="name">The name of the configuration.</param>
        /// <param name="param">The data passed for the configuration.</param>
        public void SetConfiguration(string name, params object[] param)
        {
        }

        /// <summary>
        /// Requests data from the routine with the associated name.
        /// </summary>
        /// <param name="name">The name of the configuration.</param>
        /// <returns>Data from the routine.</returns>
        public object GetConfiguration(string name)
        {
            return null;
        }

        /// <summary>
        /// The routine's coroutine logic to execute.
        /// </summary>
        /// <param name="type">The requested type of logic to execute.</param>
        /// <param name="context">Data sent to the routine from the bot for the current logic.</param>
        /// <returns>true if logic was executed to handle this type and false otherwise.</returns>
        public async Task<bool> Logic(string type, object context)
        {
            // The bot is requesting mulligan logic.
            if (type == "mulligan")
            {
                await MulliganLogic(context as MulliganData);
                return true;
            }

            // The bot is requesting emote logic.
            if (type == "emote")
            {
                await EmoteLogic(context as EmoteData);
                return true;
            }

            // The bot is requesting our turn logic.
            if (type == "our_turn")
            {
                await OurTurnLogic();
                return true;
            }

            // The bot is requesting opponent turn logic.
            if (type == "opponent_turn")
            {
                await OpponentTurnLogic();
                return true;
            }

            // The bot is requesting our turn logic.
            if (type == "our_turn_combat")
            {
                await OurTurnCombatLogic();
                return true;
            }

            // The bot is requesting opponent turn logic.
            if (type == "opponent_turn_combat")
            {
                await OpponentTurnCombatLogic();
                return true;
            }

            // The bot is requesting quest handling logic.
            if (type == "handle_quests")
            {
                await HandleQuestsLogic(context as QuestData);
                return true;
            }

            // Whatever the current logic type is, this routine doesn't implement it.
            return false;
        }

        #region Mulligan

        private int RandomMulliganThinkTime()
        {
            var random = Client.Random;
            var type = random.Next(0, 100) % 4;

            if (type == 0) return random.Next(800, 1200);
            if (type == 1) return random.Next(1200, 2500);
            if (type == 2) return random.Next(2500, 3700);
            return 0;
        }

        /// <summary>
        ///  This function is used to correct the influence of different races.
        /// </summary>
        private int CorrectHerosRanking(List<Triton.Game.Abstraction.Card> cards)
        {
            int choice = 0;
            double min = 8;
            for (int i = 0; i < cards.Count; i++)
            {
                Triton.Game.Abstraction.Card card = cards[i];
                double ranking = 0;
                try
                {
                    ranking = _mulliganRules.Find((Tuple<string, double> hero) => hero.Item1 == card.Entity.Id).Item2;
                }
                catch
                {
                }
                switch (card.Entity.Id)
                {
                    case "TB_BaconShop_HERO_49":
                        if (_includedRaces.Contains(Triton.Game.Mapping.TAG_RACE.MURLOC))
                        {
                            ranking += 0.1f;
                        }
                        if (_includedRaces.Contains(Triton.Game.Mapping.TAG_RACE.PET))
                        {
                            ranking += 0.1f;
                        }
                        break;
                }
                choice = (min >= ranking) ? i : choice;
                min = (min >= ranking) ? ranking : min;
            }
            Log.InfoFormat("[开局英雄选择] {0} 英雄排名较高 ({1}).", cards[choice].Entity.Id, min);
            return choice;
        }

        /// <summary>
        /// This task implements custom mulligan choosing logic for the bot.
        /// The user is expected to set the Mulligans list elements to true/false 
        /// to signal to the bot which cards should/shouldn't be mulliganed. 
        /// This task should also implement humanization factors, such as hovering 
        /// over cards, or delaying randomly before returning, as the mulligan 
        /// process takes place as soon as the task completes.
        /// </summary>
        /// <param name="mulliganData">An object that contains relevant data for the mulligan process.</param>
        /// <returns></returns>
        public async Task MulliganLogic(MulliganData mulliganData)
        {
            if (!botset.ForceConcedeAtMulligan)
            {
                Log.InfoFormat("[日志档案]: 开始创建");
                Hrtprozis prozis = Hrtprozis.Instance;
                prozis.clearAllNewGame();
                Silverfish.Instance.setnewLoggFile();
                Log.InfoFormat("[日志档案]: 创建完成");
            }
            // 创建日志位置
            printUtils.recordPath = string.Format(@".\Routines\BattleRoutine\Silverfish\Test\Data\对局记录\日期{0}\{1}-{2}-{3}\", DateTime.Now.ToString("yyyy-MM-dd"), DateTime.Now.ToString("HH-mm-ss"), mulliganData.UserClass, mulliganData.OpponentClass);
            if (Directory.Exists(printUtils.recordPath) == false)
            {
                Directory.CreateDirectory(printUtils.recordPath);
            }

            Log.InfoFormat("[开局英雄选择] {0} 对阵 {1}.", mulliganData.UserClass, mulliganData.OpponentClass);
            var count = mulliganData.Cards.Count;

            if (this.behave.BehaviorName() != BattleRoutineSettings.Instance.DefaultBehavior)
            {
                behave = sf.getBehaviorByName(BattleRoutineSettings.Instance.DefaultBehavior);
            }
            try
            {
                _includedRaces = Triton.Game.Mapping.GameState.Get().m_availableRacesInBattlegroundsExcludingAmalgam;
                mulliganData.Mulligans[this.CorrectHerosRanking(mulliganData.Cards)] = true;
            }
            catch (Exception ex)
            {
                Log.ErrorFormat("[Mulligan] An exception occurred: {0}.", ex);
                BotManager.Stop();
                return;
            }

            var thinkList = new List<KeyValuePair<int, int>>();
            for (var i = 0; i < count; i++)
            {
                thinkList.Add(new KeyValuePair<int, int>(i % count, RandomMulliganThinkTime()));
            }
            thinkList.Shuffle();

            foreach (var entry in thinkList)
            {
                var card = mulliganData.Cards[entry.Key];

                Log.InfoFormat("[开局英雄选择] 现在开始思考选择 {0} 时间已经过去 {1} 毫秒.", card.Entity.Id, entry.Value);

                // Instant think time, skip the card.
                if (entry.Value == 0)
                    continue;

                Client.MouseOver(card.InteractPoint);

                await Coroutine.Sleep(entry.Value);
            }
        }

        #endregion

        #region Emote

        /// <summary>
        /// This task implements player emote detection logic for the bot.
        /// </summary>
        /// <param name="data">An object that contains relevant data for the emote event.</param>
        /// <returns></returns>
        public async Task EmoteLogic(EmoteData data)
        {
            Log.InfoFormat("[表情] 使用表情 [{0}].", data.Emote);

            if (data.Emote == EmoteType.GREETINGS)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_ONE);
            }
            else if (data.Emote == EmoteType.WELL_PLAYED)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_TWO);
            }
            else if (data.Emote == EmoteType.OOPS)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_THREE);
            }
            else if (data.Emote == EmoteType.THREATEN)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_FOUR);
            }
            else if (data.Emote == EmoteType.THANKS)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_FIVE);
            }
            else if (data.Emote == EmoteType.SORRY)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.BATTLEGROUNDS_VISUAL_SIX);
            }
        }

        #endregion

        #region Turn

        public async Task OurTurnCombatLogic()
        {
            Log.InfoFormat("[BattleRoutine] Our Turn Started.");
            await Coroutine.Sleep(555 + makeChoice());
            switch (dirtychoice)
            {
                case 0: TritonHs.ChooseOneClickMiddle(); break;
                case 1: TritonHs.ChooseOneClickLeft(); break;
                case 2: TritonHs.ChooseOneClickRight(); break;
            }

            dirtychoice = -1;
            await Coroutine.Sleep(555);
            Silverfish.Instance.lastpf = null;
            return;
        }

        public async Task OpponentTurnCombatLogic()
        {
            Log.Info("[BattleRoutine] Combat Taking Place.");
            await Coroutine.Sleep(250);
        }


        public void ChooseOneClick(int dirty)
        {
            if (dirty >= 0)
            {
                switch (dirtychoice)
                {
                    case 0: TritonHs.ChooseOneClickMiddle(); break;
                    case 1: TritonHs.ChooseOneClickLeft(); break;
                    default:
                        {
                            List<Card> friendlyCards = ChoiceCardMgr.Get().GetFriendlyCards();
                            if (friendlyCards.Count > dirty)
                                Client.LeftClickAt(Client.CardInteractPoint(friendlyCards[dirty]));
                            else
                                TritonHs.ChooseOneClickRight();//抉择
                            break;
                        }
                }
            }
        }

        /// <summary>
        /// Under construction.
        /// </summary>
        /// <returns></returns>
        public async Task OurTurnLogic()
        {
            //Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.GREETINGS);
            if (firstMove && "嘴臭模式".Equals(printUtils.emoteMode))
            {
                firstMove = false;
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.WELL_PLAYED);
            }
            if (firstMove && "乞讨模式".Equals(printUtils.emoteMode))
            {
                firstMove = false;
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.THANKS);
            }
            if (firstTurn && "友善模式".Equals(printUtils.emoteMode))
            {
                firstTurn = false;
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.GREETINGS);
            }
            if ("摊牌了我是脚本".Equals(printUtils.emoteMode))
            {
                EmoteType[] emoteTypes = new EmoteType[] { EmoteType.CONCEDE, EmoteType.DEATH_LINE, EmoteType.EVENT_FIRE_FESTIVAL_GREETINGS, EmoteType.EVENT_HAPPY_NEW_YEAR, EmoteType.EVENT_LUNAR_NEW_YEAR, EmoteType.EVENT_WINTER_VEIL, EmoteType.LOW_CARDS, EmoteType.MIRROR_START, EmoteType.NO_CARDS, EmoteType.SORRY, EmoteType.START, EmoteType.THINK1, EmoteType.THINK2, EmoteType.THINK3, EmoteType.TIMER };
                //EmoteType[] emoteTypes = Enum.GetValues(typeof(EmoteType)) as EmoteType[];
                Random random = new Random();
                EmoteType emote = emoteTypes[random.Next(0, emoteTypes.Length)];
                GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(emote);
            }
            if ("精神污染模式".Equals(printUtils.emoteMode))
            {
                EmoteType[] emoteTypes = new EmoteType[] { EmoteType.GREETINGS, EmoteType.THANKS, EmoteType.OOPS, EmoteType.WELL_PLAYED, EmoteType.WOW, EmoteType.THREATEN };
                Random random = new Random();
                EmoteType emote = emoteTypes[random.Next(0, emoteTypes.Length)];
                GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(emote);
            }

            if (Ai.Instance.bestmoveValue > 5000)
            {
                if ("嘴臭模式".Equals(printUtils.emoteMode))
                {
                    Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.THREATEN);
                }
                if (firstMove && ("摊牌了我是脚本".Equals(printUtils.emoteMode)) && "抱歉".Equals(printUtils.emoteMode))
                {
                    firstMove = false;
                    Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.SORRY);
                }
                if (firstMove && "友善模式".Equals(printUtils.emoteMode))
                {
                    firstMove = false;
                    Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.WELL_PLAYED);
                }
            }

            if (this.behave.BehaviorName() != BattleRoutineSettings.Instance.DefaultBehavior)
            {
                behave = sf.getBehaviorByName(BattleRoutineSettings.Instance.DefaultBehavior);
                Silverfish.Instance.lastpf = null;
            }

            if (this.learnmode && (TritonHs.IsInTargetMode() || TritonHs.IsInChoiceMode()))
            {
                await Coroutine.Sleep(50);
                return;
            }

            if (TritonHs.IsInTargetMode())
            {
                if (dirtytarget >= 0)
                {
                    Log.Info("瞄准中...");
                    HSCard source = null;
                    if (dirtyTargetSource == 9000) // 9000 = ability
                    {
                        source = TritonHs.OurHeroPowerCard;
                    }
                    else
                    {
                        source = getEntityWithNumber(dirtyTargetSource);
                    }
                    HSCard target = getEntityWithNumber(dirtytarget);

                    if (target == null)
                    {
                        Log.Error("目标为空...");
                        TritonHs.CancelTargetingMode();
                        return;
                    }

                    dirtytarget = -1;
                    dirtyTargetSource = -1;

                    if (source == null) await TritonHs.DoTarget(target);
                    else await source.DoTarget(target);

                    await Coroutine.Sleep(555);

                    return;
                }

                Log.Error("目标丢失...");
                TritonHs.CancelTargetingMode();
                return;
            }

            if (TritonHs.IsInChoiceMode())
            {
                await Coroutine.Sleep(555 + makeChoice());
                switch (dirtychoice)
                {
                    case 0: TritonHs.ChooseOneClickMiddle(); break;
                    case 1: TritonHs.ChooseOneClickLeft(); break;
                    case 2: TritonHs.ChooseOneClickRight(); break;
                }

                dirtychoice = -1;
                await Coroutine.Sleep(555);
                return;
            }

            bool sleepRetry = false;
            bool templearn = Silverfish.Instance.updateEverything(behave, 0, out sleepRetry);
            if (sleepRetry)
            {
                Log.Error("[AI] 随从没能动起来，再试一次...");
                await Coroutine.Sleep(3000);
                templearn = Silverfish.Instance.updateEverything(behave, 1, out sleepRetry);
            }

            // Display on tab
            ObservableCollection<HandCardItem> handCardList = new ObservableCollection<HandCardItem>(BattleRoutineSettings.Instance.HandCardList);
            if (BattleRoutineSettings.Instance.UseClearStatus) handCardList = new ObservableCollection<HandCardItem>();
            foreach (Handmanager.Handcard card in Silverfish.Instance.lastpf.owncards)
            {
                HandCardItem item = new HandCardItem();
                item.ChnName = card.card.chnName;
                item.AddAttack = card.addattack;
                item.AddHealth = card.addHp;
                item.Attack = card.card.Attack;
                item.Health = card.card.Health;
                item.EntityID = (int)card.card.cardIDenum;
                item.Race = (TAG_RACE)card.card.race;
                item.Position = card.position;
                handCardList.Add(item);
            }
            BattleRoutineSettings.Instance.HandCardList = handCardList;

            ObservableCollection<HandCardItem> battleGroundList = new ObservableCollection<HandCardItem>(BattleRoutineSettings.Instance.BattleGroundList);
            if (BattleRoutineSettings.Instance.UseClearStatus) battleGroundList = new ObservableCollection<HandCardItem>();
            foreach (Minion minion in Silverfish.Instance.lastpf.ownMinions)
            {
                HandCardItem item = new HandCardItem();
                item.ChnName = minion.handcard.card.chnName;
                item.GetEffect(minion);
                item.Attack = minion.Angr;
                item.Health = minion.Hp;
                item.EntityID = minion.entitiyID;
                item.Race = minion.cardRace;
                item.Position = minion.zonepos;
                battleGroundList.Add(item);
            }
            BattleRoutineSettings.Instance.BattleGroundList = battleGroundList;

            ObservableCollection<HandCardItem> baconShopList = new ObservableCollection<HandCardItem>(BattleRoutineSettings.Instance.BaconShopList);
            if (BattleRoutineSettings.Instance.UseClearStatus) baconShopList = new ObservableCollection<HandCardItem>();
            foreach (Minion minion in Silverfish.Instance.lastpf.enemyMinions)
            {
                HandCardItem item = new HandCardItem();
                item.ChnName = minion.handcard.card.chnName;
                item.GetEffect(minion);
                item.Attack = minion.Angr;
                item.Health = minion.Hp;
                item.EntityID = minion.entitiyID;
                item.Race = minion.cardRace;
                item.Position = minion.zonepos;
                baconShopList.Add(item);
            }
            BattleRoutineSettings.Instance.BaconShopList = baconShopList;

            ObservableCollection<HandCardItem> heroList = new ObservableCollection<HandCardItem>(BattleRoutineSettings.Instance.HeroList);
            if (BattleRoutineSettings.Instance.UseClearStatus) heroList = new ObservableCollection<HandCardItem>();
            foreach (Minion minion in Silverfish.Instance.heros)
            {
                HandCardItem item = new HandCardItem();
                item.ChnName = minion.handcard.card.chnName;
                item.Attack = minion.Angr;
                item.Health = minion.Hp;
                item.EntityID = minion.entitiyID;
                item.Position = minion.zonepos;
                heroList.Add(item);
            }
            BattleRoutineSettings.Instance.HeroList = heroList;

            if (templearn == true) this.printlearnmode = true;

            if (this.learnmode)
            {
                if (this.printlearnmode)
                {
                    Ai.Instance.simmulateWholeTurnandPrint();
                }
                this.printlearnmode = false;

                //do nothing
                await Coroutine.Sleep(50);
                return;
            }
            #region Check If Next Move Exists
            // 第一步操作
            var moveTodo = Ai.Instance.bestmove;

            if (moveTodo == null || moveTodo.actionType == actionEnum.endturn || Ai.Instance.bestmoveValue < -9999)
            {
                firstMove = true;
                bool doEndTurn = false;
                bool doConcede = false;
                if (HREngine.Bots.Settings.Instance.concedeMode != 0) doConcede = true;
                else doEndTurn = true;
                if (doEndTurn)
                {
                    Helpfunctions.Instance.ErrorLog("回合结束");
                    await this.EndTurnLogic();
                    return;
                }
                else if (doConcede)
                {
                    Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.WELL_PLAYED);
                    Helpfunctions.Instance.ErrorLog("我方投降...");
                    Helpfunctions.Instance.logg("投降...");
                    TritonHs.Concede(true); // Todo: 待解决，这个地方要找到bug，发现能斩杀对方，却自己投降，所以注释掉这两行代码，确保游戏永远不自己认输。
                    return;
                }
            }
            Log.Info("[BattleRoutine] Thinking about the next move...");
            hasOperation = true;
            Helpfunctions.Instance.ErrorLog("开始行动");
            if (moveTodo == null)
            {
                Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.OOPS);
                Helpfunctions.Instance.ErrorLog("实在支不出招啦. 结束当前回合");
                Log.Info("[BattleRoutine] No action is detected, end turn.");
                await this.EndTurnLogic();
                return;
            }

            #endregion

            #region Perform the Move

            // Play the move
            moveTodo.print();
            // Refresh
            if (moveTodo.actionType == actionEnum.refresh)
            {
                Log.Info("[BattleRoutine] Perform Action: Refresh the Tavern.");
                await TritonHs.DoRefresh(true);
            }
            // Upgrade
            if (moveTodo.actionType == actionEnum.upgrade)
            {
                Log.Info("[BattleRoutine] Perform Action: Upgrade the Tavern.");
                await TritonHs.DoUpgrade(true);
            }
            // Freeze
            if (moveTodo.actionType == actionEnum.freeze)
            {
                Log.Info("[BattleRoutine] Perform Action: Freeze the Tavern.");
                await TritonHs.DoFreeze(true);
            }

            //play a card form hand
            if (moveTodo.actionType == actionEnum.playcard)
            {
                Questmanager.Instance.updatePlayedCardFromHand(moveTodo.card);
                HSCard cardtoplay = getCardWithNumber(moveTodo.card.entity);
                if (cardtoplay == null)
                {
                    Log.ErrorFormat("[BattleRoutine] Error when trying to play a card (Card Not Found).");
                    Helpfunctions.Instance.ErrorLog("[提示] 实在支不出招啦");
                    return;
                }
                Log.Info("[BattleRoutine] Perform Action: Play card (" + cardtoplay.Name + ").");
                if (moveTodo.target != null)
                {
                    HSCard target = getEntityWithNumber(moveTodo.target.entitiyID);
                    if (target != null)
                    {
                        Helpfunctions.Instance.ErrorLog("使用: " + cardtoplay.Name + " (" + cardtoplay.EntityId + ") 瞄准: " + target.Name + " (" + target.EntityId + ")");
                        Helpfunctions.Instance.logg("使用: " + cardtoplay.Name + " (" + cardtoplay.EntityId + ") 瞄准: " + target.Name + " (" + target.EntityId + ") 抉择: " + moveTodo.druidchoice);
                        Log.Info("[BattleRoutine] Perform Action: Target at (" + target.Name + ").");
                        //safe targeting stuff for hsbuddy
                        dirtyTargetSource = moveTodo.card.entity;
                        dirtytarget = moveTodo.target.entitiyID;

                        await cardtoplay.Pickup();

                        if (moveTodo.card.card.type == CardDB.cardtype.MOB)
                        {
                            await cardtoplay.UseAt(moveTodo.place);
                        }
                        else if (moveTodo.card.card.type == CardDB.cardtype.WEAPON) // This fixes perdition's blade
                        {
                            await cardtoplay.UseOn(target.Card);
                        }
                        else if (moveTodo.card.card.type == CardDB.cardtype.SPELL)
                        {
                            await cardtoplay.UseOn(target.Card);
                        }
                        else
                        {
                            await cardtoplay.UseOn(target.Card);
                        }
                    }
                    else
                    {
                        Triton.Game.Mapping.GameState.Get().GetCurrentPlayer().GetHeroCard().PlayEmote(EmoteType.OOPS);
                        Helpfunctions.Instance.ErrorLog("[AI] 目标丢失，再试一次...");
                        Helpfunctions.Instance.logg("[AI] 目标 " + moveTodo.target.entitiyID + "丢失. 再试一次...");
                        Log.ErrorFormat("[BattleRoutine] Perform Action: Target Lost (" + moveTodo.target.entitiyID + ").");
                        dirtyTargetSource = -1;
                        dirtytarget = -1;
                        await Coroutine.Sleep(3000);
                    }
                    await Coroutine.Sleep(500);
                    return;
                }

                Helpfunctions.Instance.ErrorLog("使用: " + cardtoplay.Name + " (" + cardtoplay.EntityId + ") 暂时没有目标");
                Helpfunctions.Instance.logg("使用: " + cardtoplay.Name + " (" + cardtoplay.EntityId + ") 抉择: " + moveTodo.druidchoice);
                Log.Info("[BattleRoutine] Perform Action: Use card (" + cardtoplay.Name + ") with no target.");

                await cardtoplay.Pickup();

                if (moveTodo.card.card.type == CardDB.cardtype.MOB)
                {
                    await cardtoplay.UseAt(moveTodo.place);
                }
                else
                {
                    await cardtoplay.Use();
                }
                await Coroutine.Sleep(500);

                return;
            }

            // Sell the minion
            if (moveTodo.actionType == actionEnum.sellMinion)
            {
                HSCard attacker = getEntityWithNumber(moveTodo.own.entitiyID);
                HSCard target = getEntityWithNumber(TritonHs.EnemyHero.EntityId);
                if (attacker != null)
                {
                    if (target != null)
                    {
                        Log.Info("[BattleRoutine] Perform Action: Sell minion (" + attacker.Name + ").");

                        Helpfunctions.Instance.ErrorLog("随从出售: " + attacker.Name);
                        Helpfunctions.Instance.logg("随从出售: " + attacker.Name);
                        await attacker.DoDrag(target);
                    }
                    else
                    {
                        Log.Error("[BattleRoutine] Perform Action: Error when trying to sell minion (" + attacker.Name + "), enemy hero not found.");

                        Helpfunctions.Instance.ErrorLog("[AI] 出售时旅店老板目标丢失，再次重试...");
                        Helpfunctions.Instance.logg("[AI] 出售时旅店老板目标 " + TritonHs.EnemyHero.EntityId + "丢失，再次重试...");
                        await Coroutine.Sleep(3000);
                    }
                }
                else
                {
                    Log.Error("[BattleRoutine] Perform Action: Error when trying to sell minion (" + moveTodo.own.entitiyID + "), minion not found.");

                    Helpfunctions.Instance.ErrorLog("[AI] 出售失败，再次重试...");
                    Helpfunctions.Instance.logg("[AI] 出售 " + moveTodo.own.entitiyID + " 失败.再次重试...");
                }
                await Coroutine.Sleep(250);
                return;
            }
            // Buy the minion
            if (moveTodo.actionType == actionEnum.buyMinion)
            {
                HSCard attacker = getEntityWithNumber(moveTodo.own.entitiyID);
                HSCard target = getEntityWithNumber(TritonHs.OurHero.EntityId);
                if (attacker != null)
                {
                    if (target != null)
                    {
                        Log.Info("[BattleRoutine] Perform Action: Buy minion (" + attacker.Name + ").");

                        Helpfunctions.Instance.ErrorLog("随从购买: " + attacker.Name);
                        Helpfunctions.Instance.logg("随从购买: " + attacker.Name);
                        await attacker.DoDrag(target);
                    }
                    else
                    {
                        Log.Error("[BattleRoutine] Perform Action: Error when trying to buy minion (" + attacker.Name + "), enemy hero not found.");

                        Helpfunctions.Instance.ErrorLog("[AI] 随从购买时己方英雄目标丢失，再次重试...");
                        Helpfunctions.Instance.logg("[AI] 随从购买时己方英雄目标 " + TritonHs.OurHero.EntityId + "丢失，再次重试...");
                        await Coroutine.Sleep(3000);
                    }
                }
                else
                {
                    Log.Error("[BattleRoutine] Perform Action: Error when trying to buy minion (" + attacker.Name + "), minion not found.");

                    Helpfunctions.Instance.ErrorLog("[AI] 随从购买失败，再次重试...");
                    Helpfunctions.Instance.logg("[AI] 随从购买 " + moveTodo.own.entitiyID + " 失败，再次重试...");
                }
                await Coroutine.Sleep(250);
                return;
            }


            //use ability
            if (moveTodo.actionType == actionEnum.useHeroPower)
            {
                HSCard cardtoplay = TritonHs.OurHeroPowerCard;

                if (moveTodo.target != null)
                {
                    HSCard target = getEntityWithNumber(moveTodo.target.entitiyID);
                    if (target != null)
                    {
                        Log.Info("[BattleRoutine] Perform Action: Use ability (" + cardtoplay.Name + ") at (" + target.Name + ").");

                        Helpfunctions.Instance.ErrorLog("使用英雄技能: " + cardtoplay.Name + " 目标为 " + target.Name);
                        Helpfunctions.Instance.logg("使用英雄技能: " + cardtoplay.Name + " 目标为 " + target.Name + (moveTodo.druidchoice > 0 ? (" 抉择: " + moveTodo.druidchoice) : ""));
                        if (moveTodo.druidchoice > 0)
                        {
                            dirtytarget = moveTodo.target.entitiyID;
                            dirtychoice = moveTodo.druidchoice; //1=leftcard, 2= rightcard
                            choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                        }

                        dirtyTargetSource = 9000;
                        dirtytarget = moveTodo.target.entitiyID;

                        await cardtoplay.Pickup();
                        await cardtoplay.UseOn(target.Card);
                    }
                    else
                    {
                        Log.Error("[BattleRoutine] Perform Action: Error when trying to use ability (" + cardtoplay.Name + "), target lost.");

                        Helpfunctions.Instance.ErrorLog("[AI] 目标丢失，再次重试...");
                        Helpfunctions.Instance.logg("[AI] 目标 " + moveTodo.target.entitiyID + "丢失. 再次重试...");
                        await Coroutine.Sleep(3000);
                    }
                    await Coroutine.Sleep(500);
                }
                else
                {
                    Log.Info("[BattleRoutine] Perform Action: Use ability (" + cardtoplay.Name + ").");

                    Helpfunctions.Instance.ErrorLog("使用英雄技能: " + cardtoplay.Name + " 暂时没有目标");
                    Helpfunctions.Instance.logg("使用英雄技能: " + cardtoplay.Name + " 暂时没有目标" + (moveTodo.druidchoice > 0 ? (" 抉择: " + moveTodo.druidchoice) : ""));

                    if (moveTodo.druidchoice >= 1)
                    {
                        dirtychoice = moveTodo.druidchoice; //1=leftcard, 2= rightcard
                        choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                    }

                    dirtyTargetSource = -1;
                    dirtytarget = -1;

                    await cardtoplay.Pickup();
                }

                return;
            }
            #endregion

            await this.EndTurnLogic();
        }

        public async Task EndTurnLogic()
        {
            Playfield current = Ai.Instance.bestplay;
            foreach (Minion m in current.ownMinions)
            {
                foreach (Minion n in current.ownMinions)
                {
                    if (m.entitiyID != n.entitiyID && m.Angr < n.Angr && m.zonepos < n.zonepos)
                    {
                        HSCard attacker = getEntityWithNumber(m.entitiyID);
                        HSCard target = getEntityWithNumber(n.entitiyID);
                        if (attacker != null)
                        {
                            if (target != null)
                            {
                                Helpfunctions.Instance.ErrorLog("随从交换: " + attacker.Name + " 目标为: " + target.Name);
                                Helpfunctions.Instance.logg("随从交换: " + attacker.Name + " 目标为: " + target.Name);
                                Log.Info("[BattleRoutine] Perform Action: Change position (" + attacker.Name + ") => (" + target.Name + ").");

                                await attacker.DoDrag(target);
                            }
                            else
                            {
                                Helpfunctions.Instance.ErrorLog("[AI] 目标丢失");
                            }
                        }
                        else
                        {
                            Helpfunctions.Instance.ErrorLog("[AI] 交换失败");
                        }
                    }
                }
            }
            if (hasOperation)
            {
                hasOperation = false;
                Log.Info("[BattleRoutine] End of Operation.");
            }
                
            await Coroutine.Sleep(250);
        }

        private int makeChoice()
        {
            if (dirtychoice < 1)
            {
                var ccm = ChoiceCardMgr.Get();
                var lscc = ccm.m_lastShownChoiceState;
                GAME_TAG choiceMode = GAME_TAG.CHOOSE_ONE;
                int sourceEntityId = -1;
                CardDB.cardIDEnum sourceEntityCId = CardDB.cardIDEnum.None;
                if (lscc != null)
                {
                    sourceEntityId = lscc.m_sourceEntityId;
                    Entity entity = GameState.Get().GetEntity(lscc.m_sourceEntityId);
                    sourceEntityCId = CardDB.Instance.cardIdstringToEnum(entity.GetCardId());
                    if (entity != null)
                    {
                        var sourceCard = entity.GetCard();
                        if (sourceCard != null)
                        {
                            if (sourceCard.GetEntity().HasTag(GAME_TAG.DISCOVER))
                            {
                                choiceMode = GAME_TAG.DISCOVER;
                                dirtychoice = -1;
                            }
                            else if (sourceCard.GetEntity().HasTag(GAME_TAG.ADAPT))
                            {
                                choiceMode = GAME_TAG.ADAPT;
                                dirtychoice = -1;
                            }
                        }
                    }
                }

                Ai ai = Ai.Instance;
                List<Handmanager.Handcard> discoverCards = new List<Handmanager.Handcard>();
                double bestDiscoverValue = -2000000;
                var choiceCardMgr = ChoiceCardMgr.Get();
                var cards = choiceCardMgr.GetFriendlyCards();

                for (int i = 0; i < cards.Count; i++)
                {
                    var hc = new Handmanager.Handcard();
                    hc.card = CardDB.Instance.getCardDataFromID(CardDB.Instance.cardIdstringToEnum(cards[i].GetCardId()));
                    hc.position = 100 + i;
                    hc.entity = cards[i].GetEntityId();
                    hc.manacost = hc.card.calculateManaCost(ai.nextMoveGuess);
                    discoverCards.Add(hc);
                }

                int sirFinleyChoice = -1;
                if (ai.bestmove == null) Log.ErrorFormat("[提示] 没有获得卡牌数据");
                // 芬利爵士的发现
                else if (ai.bestmove.actionType == actionEnum.playcard && ai.bestmove.card.card.name == CardDB.cardName.sirfinleymrrgglton)
                {
                    sirFinleyChoice = ai.botBase.getSirFinleyPriority(discoverCards);
                }

                DateTime tmp = DateTime.Now;
                int discoverCardsCount = discoverCards.Count;
                if (sirFinleyChoice != -1) dirtychoice = sirFinleyChoice;
                else
                {
                    int dirtyTwoTurnSim = ai.mainTurnSimulator.getSecondTurnSimu();
                    ai.mainTurnSimulator.setSecondTurnSimu(true, 50);
                    using (TritonHs.Memory.ReleaseFrame(true))
                    {
                        Playfield testPl = new Playfield();
                        Playfield basePlf = new Playfield(ai.nextMoveGuess);
                        for (int i = 0; i < discoverCardsCount; i++)
                        {
                            Playfield tmpPlf = new Playfield(basePlf);
                            Playfield nextPlf = new Playfield(basePlf);
                            Playfield featurePlf = new Playfield(basePlf);

                            tmpPlf.isLethalCheck = false;
                            featurePlf.isLethalCheck = false;
                            nextPlf.mana = tmpPlf.mana + 1 > 10 ? 10 : tmpPlf.mana + 1;
                            featurePlf.mana = 10;

                            double bestval = bestDiscoverValue;
                            switch (choiceMode)
                            {
                                // 发现牌考虑当前回合、下回合和未来收益，权重5：3：2吧
                                case GAME_TAG.DISCOVER:
                                    switch (ai.bestmove.card.card.name)
                                    {
                                        case CardDB.cardName.eternalservitude:
                                        case CardDB.cardName.freefromamber:
                                            //Minion m = tmpPlf.createNewMinion(discoverCards[i], tmpPlf.ownMinions.Count, true);
                                            //tmpPlf.ownMinions[tmpPlf.ownMinions.Count - 1] = m;
                                            tmpPlf.callKid(discoverCards[i].card, tmpPlf.ownMinions.Count - 1, true);
                                            nextPlf.callKid(discoverCards[i].card, tmpPlf.ownMinions.Count - 1, true);
                                            featurePlf.callKid(discoverCards[i].card, tmpPlf.ownMinions.Count - 1, true);
                                            bestval = ai.mainTurnSimulator.doallmoves(tmpPlf) * 0.5f + ai.mainTurnSimulator.doallmoves(nextPlf) * 0.3f + ai.mainTurnSimulator.doallmoves(featurePlf) * 0.2f;
                                            break;
                                        // 芬利爵士
                                        case CardDB.cardName.sirfinleymrrgglton:
                                            ai.mainTurnSimulator.doallmoves(tmpPlf);
                                            bestval = ai.botBase.getSirFinleyPriority(discoverCards[i].card);
                                            switch (discoverCards[i].card.name)
                                            {
                                                case CardDB.cardName.demonclaws:
                                                case CardDB.cardName.shapeshift:
                                                case CardDB.cardName.fireblast:
                                                case CardDB.cardName.daggermastery:
                                                    if (tmpPlf.enemyHero.Hp <= 1) bestval += 100; break;
                                                    if (tmpPlf.enemyHero.Hp <= 10) bestval += 5; break;
                                                case CardDB.cardName.steadyshot:
                                                    if (tmpPlf.enemyHero.Hp <= 1) bestval += 100; break;
                                                    if (tmpPlf.enemyHero.Hp <= 10) bestval += 5; break;
                                                case CardDB.cardName.lifetap:
                                                    if (tmpPlf.owncards.Count <= 3) bestval += 5; break;
                                            }
                                            break;
                                        default:
                                            tmpPlf.drawACard(discoverCards[i].card.cardIDenum, true, true);
                                            nextPlf.drawACard(discoverCards[i].card.cardIDenum, true, true);
                                            featurePlf.drawACard(discoverCards[i].card.cardIDenum, true, true);
                                            //tmpPlf.owncards[tmpPlf.owncards.Count - 1] = discoverCards[i];
                                            bestval = ai.mainTurnSimulator.doallmoves(tmpPlf) * 0.5f + ai.mainTurnSimulator.doallmoves(nextPlf) * 0.3f + ai.mainTurnSimulator.doallmoves(featurePlf) * 0.2f;
                                            bestval += ai.botBase.getDiscoverVal(discoverCards[i].card, tmpPlf);
                                            break;
                                    }
                                    Helpfunctions.Instance.ErrorLog(discoverCards[i].card.chnName + "最优场面" + bestval);
                                    break;
                                case GAME_TAG.ADAPT:
                                    bool found = false;
                                    foreach (Minion m in tmpPlf.ownMinions)
                                    {
                                        if (m.entitiyID == sourceEntityId)
                                        {
                                            bool forbidden = false;
                                            switch (discoverCards[i].card.cardIDenum)
                                            {
                                                case CardDB.cardIDEnum.UNG_999t6: if (m.taunt) forbidden = true; break;
                                                case CardDB.cardIDEnum.UNG_999t7: if (m.windfury) forbidden = true; break;
                                                case CardDB.cardIDEnum.UNG_999t8: if (m.divineshild) forbidden = true; break;
                                                case CardDB.cardIDEnum.UNG_999t10: if (m.stealth) forbidden = true; break;
                                                case CardDB.cardIDEnum.UNG_999t13: if (m.poisonous) forbidden = true; break;
                                            }
                                            if (forbidden) bestval = -2000000;
                                            else
                                            {
                                                discoverCards[i].card.sim_card.onCardPlay(tmpPlf, true, m, 0);
                                                bestval = ai.mainTurnSimulator.doallmoves(tmpPlf);
                                            }
                                            found = true;
                                            break;
                                        }
                                    }
                                    if (!found) Log.ErrorFormat("[AI] sourceEntityId is missing");
                                    break;
                            }
                            if (bestDiscoverValue <= bestval)
                            {
                                bestDiscoverValue = bestval;
                                dirtychoice = i;
                            }
                        }
                    }
                    ai.mainTurnSimulator.setSecondTurnSimu(true, dirtyTwoTurnSim);
                }
                if (sourceEntityCId == CardDB.cardIDEnum.UNG_035) dirtychoice = new Random().Next(0, 2);
                if (dirtychoice == 0) dirtychoice = 1;
                else if (dirtychoice == 1) dirtychoice = 0;
                int ttf = (int)(DateTime.Now - tmp).TotalMilliseconds;
                Helpfunctions.Instance.ErrorLog("发现卡牌: " + dirtychoice + (discoverCardsCount > 1 ? " " + discoverCards[1].card.chnName : "") + (discoverCardsCount > 0 ? " " + discoverCards[0].card.chnName : "") + (discoverCardsCount > 2 ? " " + discoverCards[2].card.chnName : ""));
                Helpfunctions.Instance.logg("发现卡牌: " + dirtychoice + (discoverCardsCount > 1 ? " " + discoverCards[1].card.cardIDenum : "") + (discoverCardsCount > 0 ? " " + discoverCards[0].card.cardIDenum : "") + (discoverCardsCount > 2 ? " " + discoverCards[2].card.cardIDenum : ""));
                if (ttf < 3000) return (new Random().Next(ttf < 1300 ? 1300 - ttf : 0, 3100 - ttf));
            }
            else
            {
                Helpfunctions.Instance.logg("选择这张卡牌: " + dirtychoice);
                return (new Random().Next(1100, 3200));
            }
            return 0;
        }

        /// <summary>
        /// Under construction.
        /// </summary>
        /// <returns></returns>
        public async Task OpponentTurnLogic()
        {
            Log.InfoFormat("[对手回合]");


        }

        #endregion


        #region Handle Quests

        /// <summary>
        /// Under construction.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task HandleQuestsLogic(QuestData data)
        {
            Log.InfoFormat("[处理日常任务]");

            // Loop though all quest tiles.
            foreach (var questTile in data.QuestTiles)
            {
                // If we can't cancel a quest, we shouldn't try to.
                if (questTile.IsCancelable)
                {
                    if (BattleRoutineSettings.Instance.QuestIdsToCancel.Contains(questTile.Achievement.Id))
                    {
                        // Mark the quest tile to be canceled.
                        questTile.ShouldCancel = true;

                        StringBuilder questsInfo = new StringBuilder("", 1000);
                        questsInfo.Append("[处理日常任务] 任务列表: ");
                        int qNum = data.QuestTiles.Count;
                        for (int i = 0; i < qNum; i++)
                        {
                            var q = data.QuestTiles[i].Achievement;
                            if (q.RewardData.Count > 0)
                            {
                                questsInfo.Append("[").Append(q.RewardData[0].Count).Append("x ").Append(q.RewardData[0].Type).Append("] ");
                            }
                            questsInfo.Append(q.Name);
                            if (i < qNum - 1) questsInfo.Append(", ");
                        }
                        questsInfo.Append(". 尝试取消任务: ").Append(questTile.Achievement.Name);
                        Log.InfoFormat(questsInfo.ToString());
                        await Coroutine.Sleep(new Random().Next(4000, 8000));
                        return;
                    }
                }
                else if (BattleRoutineSettings.Instance.QuestIdsToCancel.Count > 0)
                {
                    Log.InfoFormat("取消任务失败.");
                }
            }
        }

        #endregion

        #endregion

        #region Override of Object

        /// <summary>
        /// ToString override.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name + ": " + Description;
        }

        #endregion

        private void GameEventManagerOnGameOver(object sender, GameOverEventArgs gameOverEventArgs)
        {
            firstTurn = true;
            Log.InfoFormat("[游戏结束] {0}{2} => {1}.", gameOverEventArgs.Result,
                GameEventManager.Instance.LastGamePresenceStatus, gameOverEventArgs.Conceded ? " [conceded]" : "");
        }
        //标记一下
        private void GameEventManagerOnNewGame(object sender, NewGameEventArgs newGameEventArgs)
        {
            Log.InfoFormat("[Set new log file:] Start");
            Hrtprozis prozis = Hrtprozis.Instance;
            prozis.clearAllNewGame();
            Silverfish.Instance.setnewLoggFile();
            Log.InfoFormat("[Set new log file:] End");
        }

        private void GameEventManagerOnQuestUpdate(object sender, QuestUpdateEventArgs questUpdateEventArgs)
        {
            Log.InfoFormat("[任务刷新]");
            foreach (var quest in TritonHs.CurrentQuests)
            {
                Log.InfoFormat("[任务刷新][{0}]{1}: {2} ({3} / {4}) [{6}x {5}]", quest.Id, quest.Name, quest.Description, quest.CurProgress,
                    quest.MaxProgress, quest.RewardData[0].Type, quest.RewardData[0].Count);
            }
        }

        private void GameEventManagerOnArenaRewards(object sender, ArenaRewardsEventArgs arenaRewardsEventArgs)
        {
            Log.InfoFormat("[竞技场奖励]");
            foreach (var reward in arenaRewardsEventArgs.Rewards)
            {
                Log.InfoFormat("[竞技场奖励] {1}x {0}.", reward.Type, reward.Count);
            }
        }

        private HSCard getEntityWithNumber(int number)
        {
            foreach (HSCard e in getallEntitys())
            {
                if (number == e.EntityId) return e;
            }
            return null;
        }

        private HSCard getCardWithNumber(int number)
        {
            foreach (HSCard e in getallHandCards())
            {
                if (number == e.EntityId) return e;
            }
            return null;
        }

        private List<HSCard> getallEntitys()
        {
            var result = new List<HSCard>();
            HSCard ownhero = TritonHs.OurHero;
            HSCard enemyhero = TritonHs.EnemyHero;
            HSCard ownHeroAbility = TritonHs.OurHeroPowerCard;
            List<HSCard> list2 = TritonHs.GetCards(CardZone.Battlefield, true);
            List<HSCard> list3 = TritonHs.GetCards(CardZone.Battlefield, false);

            result.Add(ownhero);
            result.Add(enemyhero);
            result.Add(ownHeroAbility);

            result.AddRange(list2);
            result.AddRange(list3);

            return result;
        }

        private List<HSCard> getallHandCards()
        {
            List<HSCard> list = TritonHs.GetCards(CardZone.Hand, true);
            return list;
        }
    }
}
